import sys
import serial.tools.list_ports
import os
import re
import subprocess
from PyQt5.QtWidgets import (QApplication, QTableWidget, QTableWidgetItem, 
                             QVBoxLayout, QWidget, QProgressBar, QPushButton, 
                             QCheckBox, QMessageBox, QHBoxLayout, QFileDialog)
from PyQt5.QtCore import QThread, pyqtSignal, QObject

class DownloadWorker(QObject):
    finished = pyqtSignal()  # 定义完成信号
    output_ready = pyqtSignal(str, str)  # 定义输出信号，携带端口和输出信息参数
    progress_updated = pyqtSignal(str, int)  # 定义进度更新信号，携带端口和进度参数
    upgrade_status_updated = pyqtSignal(str, str)  # 新增信号，更新升级状态

    def __init__(self, port, image_files):
        super().__init__()
        self.port = port
        self.image_files = image_files
        

    def run(self):
        """执行下载操作"""
        exe_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'Bin', 'DownloadShell.exe'))
        
        for image_file in self.image_files:
            command = f'"{exe_path}" -dl {self.port.split("COM")[-1]} "{image_file}"'
            self.execute_command(command)
        
        # 完成状态更新
        self.upgrade_status_updated.emit(self.port, "成功")
        self.finished.emit()

        self.finished.emit()

    def execute_command(self, command):
        """执行单个命令并发送输出"""
        process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
        total_steps = 43  # 总步骤

        for stdout_line in iter(process.stdout.readline, ""):
            if stdout_line.strip():
                # 如果输出是百分比，如10.13%，则不输出文本信息
                if not re.match(r'\d+\.\d+%', stdout_line.strip()):
                    self.output_ready.emit(self.port, stdout_line.strip())
                # 如果输出包含 fail 字样，则更新升级状态为失败
                if 'fail' in stdout_line.strip().lower():
                    self.upgrade_status_updated.emit(self.port, "失败")
                    break
                self.update_progress(stdout_line.strip(), total_steps)
        
        process.stdout.close()
        process.wait()

    def update_progress(self, output_line, total_steps):
        """更新进度条"""
        match = re.search(r'\[(\d+)/(\d+)\] pass\.', output_line)
        if match:
            completed = int(match.group(1))  # 获取完成的步骤
            percentage = int((completed / total_steps) * 100)  # 计算百分比
            self.progress_updated.emit(self.port, percentage)  # 发送进度

class MyWindow(QWidget):
    def __init__(self, port_row_dict={}):
        super().__init__()
        self.setWindowTitle("升级列表")
        self.port_row_dict = port_row_dict
        # 存储线程和工作者的列表
        self.download_threads = []
        self.download_workers = []
        
        
        # 创建表格
        self.table = QTableWidget()
        self.table.setColumnCount(6)  # 设置列数为6，增加选择框列
        self.table.setHorizontalHeaderLabels(["选择", "端口名", "升级状态", "升级信息", "进度条", "升级时间"])

        # 创建复选框和标签
        self.select_all_checkbox = QCheckBox("全选")
        self.select_all_checkbox.stateChanged.connect(self.toggle_select_all)

        # 创建一个按钮用于识别端口
        self.recognize_button = QPushButton("识别端口")
        self.recognize_button.clicked.connect(self.recognize_ports)

        # 创建一个按钮用于开始升级
        self.upgrade_button = QPushButton("开始升级")
        self.upgrade_button.clicked.connect(self.start_upgrade)


        self.select_image_button = QPushButton("选择镜像文件")
        self.select_image_button.clicked.connect(self.select_image_files)
        

        # 布局
        layout = QVBoxLayout()
        
        # 添加复选框和识别按钮到布局
        h_layout = QHBoxLayout()
        h_layout.addWidget(self.select_all_checkbox)
        h_layout.addWidget(self.recognize_button)

        layout.addLayout(h_layout)  # 将水平布局添加到主布局
        layout.addWidget(self.table)
        layout.addWidget(self.upgrade_button)
        layout.addWidget(self.select_image_button)
        self.setLayout(layout)

        # 设置表格自适应列宽
        self.table.horizontalHeader().setStretchLastSection(True)

        # 初始化进度条
        self.progress_bars = {}

    def toggle_select_all(self):
        # 切换所有复选框的状态
        select_all = self.select_all_checkbox.isChecked()
        for row in range(self.table.rowCount()):
            checkbox = self.table.cellWidget(row, 0)  # 获取复选框
            checkbox.setChecked(select_all)

    def add_row(self, port_name, upgrade_status, upgrade_info, progress, upgrade_time):
        row_position = self.table.rowCount()
        self.table.insertRow(row_position)

        # 添加复选框
        checkbox = QCheckBox()
        self.table.setCellWidget(row_position, 0, checkbox)

        self.table.setItem(row_position, 1, QTableWidgetItem(port_name))
        self.table.setItem(row_position, 2, QTableWidgetItem(upgrade_status))
        self.table.setItem(row_position, 3, QTableWidgetItem(upgrade_info))  # 确保你在这创建了一个 QTableWidgetItem
        # upgrade_info的宽度设置为行宽度的30%
        self.table.setColumnWidth(3, int(self.table.width() * 0.3))
        
        # 添加进度条并存储引用
        progress_bar = QProgressBar()
        progress_bar.setValue(progress)
        
        self.table.setCellWidget(row_position, 4, progress_bar)  # 这会把进度条放在第 4 列
        # 进度条的宽度
        self.table.setColumnWidth(4, int(self.table.width() * 0.3))
        self.table.setItem(row_position, 5, QTableWidgetItem(upgrade_time))

    


    def update_output(self, port, message):
        row = self.find_row_by_port(port)
        if row is not None:
            # 更新到“升级信息”列
            self.table.item(row, 3).setText(message)  # 确保你在第三列（索引为3）更新信息


    def recognize_ports(self):
        # 获取当前可用串口
        ports = serial.tools.list_ports.comports()
        self.table.setRowCount(0)  # 清空当前表格
        for port in ports:
            port_name = port.device  # 端口名
            upgrade_status = "未开始"  # 初始升级状态
            upgrade_info = "待升级"  # 初始升级信息
            progress = 0  # 初始进度
            upgrade_time = "N/A"  # 初始升级时间

            self.add_row(port_name, upgrade_status, upgrade_info, progress, upgrade_time)

    def select_image_files(self):
        """选择镜像文件 (.mimgx)"""
        files, _ = QFileDialog.getOpenFileNames(self, "选择镜像文件", "", "镜像文件 (*.mimgx)")
        if files:
            self.image_files = files
            QMessageBox.information(self, "选择文件", f"选择的文件:\n{', '.join(self.image_files)}")

    def start_upgrade(self):
        selected_ports = []
        for row in range(self.table.rowCount()):
            checkbox = self.table.cellWidget(row, 0)  # 获取复选框
            if checkbox.isChecked():
                port_name = self.table.item(row, 1).text()  # 获取端口名
                selected_ports.append(port_name)
                self.port_row_dict[port_name] = row  # 将端口和row绑定保存

        if selected_ports:
            QMessageBox.information(self, "升级开始", f"准备升级的端口: {', '.join(selected_ports)}")
        else:
            QMessageBox.warning(self, "警告", "请至少选择一个端口进行升级。")
            return  # 直接返回，避免后续代码执行

        for port in selected_ports:
            # 为每个端口创建一个新的线程和工作线程
            download_thread = QThread()
            download_worker = DownloadWorker(port, self.image_files)

            # 将信号连接到主线程的输出界面
            download_worker.output_ready.connect(self.update_output)
            download_worker.progress_updated.connect(self.update_progress_bar)
            download_worker.upgrade_status_updated.connect(self.update_upgrade_status)

            # 确保线程完成后清理
            download_worker.finished.connect(download_thread.quit)  # 线程完成时退出
            download_worker.finished.connect(download_worker.deleteLater)  # 删除工作对象
            download_thread.finished.connect(download_thread.deleteLater)  # 删除线程对象

            # 移动工作线程到新的线程中
            download_worker.moveToThread(download_thread)

            # 连接信号以执行下载
            download_thread.started.connect(download_worker.run)

            # 启动线程
            download_thread.start()
            self.update_upgrade_status(port, "升级中")
            # 保存线程和工作者的引用
            self.download_threads.append(download_thread)
            self.download_workers.append(download_worker)




    def update_output(self, port, message):
        row = self.find_row_by_port(port)
        if row is not None:
            self.table.item(row, 3).setText(message)  # 更新信息

    def update_progress_bar(self, port, progress):
        row = self.find_row_by_port(port)
        if row is not None:
            progress_bar = self.table.cellWidget(row, 4)  # 获取进度条
            if progress_bar is not None:  # 检查进度条是否存在
                progress_bar.setValue(progress)  # 更新进度条的值

    def update_upgrade_status(self, port, status):
        # 更新升级状态显示
        row = self.find_row_by_port(port)
        if row is not None:
            self.table.item(row, 2).setText(status)  # 更新状态

    def find_row_by_port(self, port):
        for row in range(self.table.rowCount()):
            if self.table.item(row, 1).text() == port:
                return row
        return None

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MyWindow()
    # 设置窗口大小为屏幕大小的 80%
    window.resize(int(app.primaryScreen().size().width() * 0.8), int(app.primaryScreen().size().height() * 0.8))
    window.show()
    sys.exit(app.exec_())
